/*
Art and Comics Blog Critical Mass Emacs Free Stuff Games Gnu/Linux Home Learn Japanese Links Montreal Music News Some Essays Statistics

Node:Example programs, Next:A note from the original author, Previous:Debugging, Up:Top

Example programs

The aim of this section is to provide a substantial example of C programming, using input from and output to disk, GNU-style long options, and the linked list data structure (including insertion, deletion, and sorting of nodes).
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <argp.h>

#define NAME_LEN 100
#define ADDR_LEN 500

const char *argp_program_version =
"bigex 1.0";

const char *argp_program_bug_address =
"<bug-gnu-utilsgnu.org>";


/* This structure is used by main to communicate with parse_opt. */
struct arguments
{
	char *args[1];            /* No arguments to this function */
	int verbose;              /* The -v flag */
	char *infile;             /* Argument for -i */
	char *outfile;            /* Argument for -o */

};


struct personal_data
{
	char name[NAME_LEN];
	char address[ADDR_LEN];
	struct personal_data *next;

};


/*
 *   OPTIONS.  Field 1 in ARGP.
 *     Order of fields: {NAME, KEY, ARG, FLAGS, DOC}.
 *     */
static struct argp_option options[] =
{
	{"verbose", 'v', 0, 0, "Produce verbose output"},

	{"input",   'i', "INFILE",  0,
		"Read addresses from INFILE"},

	{"output",  'o', "OUTFILE", 0,
		"Output to OUTFILE instead of to standard output"},

	{0}

};


/*
 *   PARSER. Field 2 in ARGP.
 *     Order of parameters: KEY, ARG, STATE.
 *     */
	static error_t
parse_opt (int key, char *arg, struct argp_state *state)
{
	struct arguments *arguments = state->input;

	switch (key)
	{
		case 'v':
			arguments->verbose = 1;
			break;
		case 'i':
			arguments->infile = arg;
			break;
		case 'o':
			arguments->outfile = arg;
			break;
		case ARGP_KEY_ARG:
			if (state->arg_num >= 1)
			{
				argp_usage(state);

			}
			arguments->args[state->arg_num] = arg;
			break;
		case ARGP_KEY_END:
			if (state->arg_num < 1)
			{
				argp_usage (state);

			}
			break;
		default:
			return ARGP_ERR_UNKNOWN;

	}
	return 0;

}


/*
 *   ARGS_DOC. Field 3 in ARGP.
 *     A description of the non-option command-line arguments
 *       that we accept.
 *       */
static char args_doc[] = "ARG";


/*
 *   DOC.  Field 4 in ARGP.
 *     Program documentation.
 *     */
static char doc[] =
"bigex -- Add ARG new names to an address book file.\vThe largest code example in the GNU C Tutorial.";


/*
 *   The ARGP structure itself.
 *   */
static struct argp argp = {options, parse_opt, args_doc, doc};


	struct personal_data *
new_empty_node()
{
	struct personal_data *new_node;

	new_node =  (struct personal_data*)
		malloc (sizeof (struct personal_data));

	strcpy (new_node->name, "");
	strcpy (new_node->address, "");
	new_node->next = NULL;

	return new_node;

}


	struct personal_data *
create_node()
{
	int bytes_read;
	int nbytes;

	struct personal_data *current_node;
	char *name;
	char *address;

	current_node = new_empty_node();

	puts ("Name?");
	nbytes = NAME_LEN;
	name = (char *) malloc (nbytes + 1);
	bytes_read = getline (&name, &nbytes, stdin);
	if (bytes_read == -1)
	{
		puts ("ERROR!");

	}
	else
	{
		strncpy (current_node->name, name, NAME_LEN);
		free (name);

	}

	puts ("Address?");
	nbytes = ADDR_LEN;
	address = (char *) malloc (nbytes + 1);
	bytes_read = getline (&address, &nbytes, stdin);
	if (bytes_read == -1)
	{
		puts ("ERROR!");

	}
	else
	{
		strncpy (current_node->address, address, ADDR_LEN);
		free (address);

	}

	printf("\n");
	return current_node;

}



	struct personal_data *
find_end_node (struct personal_data *current_node)
{
	if (current_node->next == NULL)
	{
		return current_node;

	}
	else
	{
		return find_end_node (current_node->next);

	}

}


	int
list_length (struct personal_data *root)
{
	struct personal_data *current_node;
	int count = 0;

	current_node = root;

	while (current_node->next != NULL)
	{
		current_node = current_node->next;
		count++;

	}
	return count;

}


	struct personal_data *
find_node (struct personal_data *root,
		int node_wanted)
{
	struct personal_data *current_node;
	int index = 0;

	current_node = root;

	while ((index < node_wanted) && (current_node->next != NULL))
	{
		current_node = current_node->next;
		index++;

	}
	return current_node;

}



delete_node (struct personal_data *root,
		int location)
{
	struct personal_data *previous_node;
	struct personal_data *current_node;

	previous_node = find_node (root, location - 1);
	current_node = find_node (root, location);
	previous_node->next = current_node->next;

}



insert_node (struct personal_data *root,
		struct personal_data *new_node,
		int location)
{
	struct personal_data *temp_ptr;
	struct personal_data *previous_node;

	previous_node = find_node (root, location - 1);
	temp_ptr = previous_node->next;

	previous_node->next = new_node;
	new_node->next = temp_ptr;

}


swap_nodes (struct personal_data *root, int a, int b)
{
	int temp;
	struct personal_data *node_a;
	struct personal_data *node_b;
	struct personal_data *temp_node;

	if (a > b)
	{
		temp = a;
		a = b;
		b = temp;

	}

	node_b = find_node (root, b);
	delete_node (root, b);

	node_a = find_node (root, a);
	delete_node (root, a);

	insert_node (root, node_b, a);
	insert_node (root, node_a, b);

}


sort_list (struct personal_data *root)
{
	int i, j, list_len, diff;

	list_len = list_length (root);
	for (i=2; i<=list_len; i++)
	{
		j = i;
		while (strcmp ( (find_node(root, j))->name,
					(find_node(root, j-1))->name) < 0)
		{
			swap_nodes (root, j, j-1);
			j--;

		}

	}

}


print_node (struct personal_data *current_node,
		FILE *save_stream)
{
	fprintf (save_stream, "%s%s",
			current_node->name,
			current_node->address);

}


print_list (struct personal_data *current_node,
		FILE *save_stream)
{
	print_node (current_node, save_stream);

	if (current_node->next != NULL)
	{
		print_list (current_node->next, save_stream);

	}

}




	struct personal_data *
read_node (FILE *instream)
{
	int bytes_read;
	int nbytes;

	struct personal_data *current_node;
	char *name;
	char *address;
	char *blankline;
	int read_err = 0;

	current_node = new_empty_node();

	nbytes = NAME_LEN;
	name = (char *) malloc (nbytes + 1);
	bytes_read = getline (&name, &nbytes, instream);
	if (bytes_read == -1)
	{
		read_err = 1;

	}
	else
	{
		puts (name);
		strncpy (current_node->name, name, NAME_LEN);
		free (name);

	}

	nbytes = ADDR_LEN;
	address = (char *) malloc (nbytes + 1);
	bytes_read = getline (&address, &nbytes, instream);
	if (bytes_read == -1)
	{
		read_err = 1;

	}
	else
	{
		puts (address);
		strncpy (current_node->address, address, ADDR_LEN);
		free (address);

	}

	if (read_err)
	{
		return NULL;

	}
	else
	{
		return current_node;

	}

}


	struct personal_data *
read_file (char *infile)
{
	FILE *input_stream = NULL;
	struct personal_data *root;
	struct personal_data *end_node;
	struct personal_data *current_node;

	root = new_empty_node();
	end_node = root;

	input_stream = fopen (infile, "r");
	if (input_stream)
	{
		while (current_node = read_node (input_stream))
		{
			end_node->next = current_node;
			end_node = current_node;
			end_node->next = NULL;

		}

	}
	return root;

}



/*
 *   The main function.
 *     Notice how now the only function call needed to process
 *       all command-line options and arguments nicely
 *         is argp_parse.
 *         */
	int
main (int argc, char **argv)
{
	struct arguments arguments;
	struct personal_data *root;
	struct personal_data *end_node;
	struct personal_data *current_node;
	int i, newnum;
	FILE *save_stream;


	/* Set argument defaults */
	arguments.infile = NULL;
	arguments.outfile = NULL;
	arguments.verbose = 0;

	/* Where the magic happens */
	argp_parse (&argp, argc, argv, 0, 0, &arguments);

	if (arguments.infile)
	{
		root = read_file (arguments.infile);
		end_node = find_end_node (root);

	}
	else
	{
		root = new_empty_node();
		end_node = root;

	}

	/* Where do we send output? */
	if (arguments.outfile)
		save_stream = fopen (arguments.outfile, "w");
	else
		save_stream = stdout;

	newnum = atoi (arguments.args[0]);

	for (i = 1; i <= newnum; i++)
	{
		current_node = create_node();
		end_node->next = current_node;
		end_node = current_node;
		end_node->next = NULL;

	}

	sort_list (root);
	print_list (root->next, save_stream);

	/* Close stream; skip error-checking for brevity of example */
	fclose (save_stream);

	/* If in verbose mode, print song stanza */
	if (arguments.verbose)
	{};

	return 0;

}

